package cn.chiship.sdk.third.storage.impl;

import cn.chiship.sdk.core.base.BaseResult;
import cn.chiship.sdk.core.util.StringUtil;
import cn.chiship.sdk.third.core.model.tencent.TencentCosConfigModel;
import cn.chiship.sdk.third.storage.FileStorageService;

import com.alibaba.fastjson.JSON;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.exception.MultiObjectDeleteException;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.TransferManager;
import com.qcloud.cos.transfer.TransferManagerConfiguration;
import com.qcloud.cos.transfer.Upload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.*;

/**
 * 腾讯云存储实现类
 *
 * @author lj
 */
public class TencentCosServiceImpl implements FileStorageService {

	private static final Logger LOGGER = LoggerFactory.getLogger(TencentCosServiceImpl.class);

	private static final String S1 = "TencentOssServiceImpl发生错误:{}";

	private TencentCosConfigModel tencentOssConfigModel;

	public TencentCosServiceImpl(TencentCosConfigModel tencentOssConfigModel) {
		this.tencentOssConfigModel = tencentOssConfigModel;
	}

	public TencentCosServiceImpl() {
		this.tencentOssConfigModel = new TencentCosConfigModel();
	}

	@Override
	public BaseResult upload(InputStream inputStream, String fileName) {
		return upload(inputStream, null, fileName);
	}

	@Override
	public BaseResult upload(InputStream inputStream, String catalog, String fileName) {
		TransferManager transferManager = createTransferManager();
		ObjectMetadata objectMetadata = new ObjectMetadata();
		String objectKey;
		if (!StringUtil.isNullOrEmpty(catalog)) {
			objectKey = this.tencentOssConfigModel.getRoot() + "/" + catalog + "/" + fileName;
		}
		else {
			objectKey = this.tencentOssConfigModel.getRoot() + "/" + fileName;
		}
		PutObjectRequest putObjectRequest = new PutObjectRequest(this.tencentOssConfigModel.getBuckName(), objectKey,
				inputStream, objectMetadata);
		putObjectRequest.setStorageClass(StorageClass.Standard_IA);
		try {
			Upload upload = transferManager.upload(putObjectRequest);
			UploadResult uploadResult = upload.waitForUploadResult();
			String fileUrl = String.format("http://%s.cos.%s.myqcloud.com/%s", uploadResult.getBucketName(),
					tencentOssConfigModel.getOssEndPort(), uploadResult.getKey());
			shutdownTransferManager(transferManager);
			LOGGER.info("文件上传成功");
			return BaseResult.ok(fileUrl);
		}
		catch (CosServiceException e) {
			LOGGER.error(S1, e.getErrorMessage());
			return BaseResult.error(e.getErrorMessage());
		}
		catch (CosClientException e) {
			LOGGER.error(S1, e.getMessage());
			return BaseResult.error(e.getMessage());
		}
		catch (InterruptedException e) {
			return BaseResult.error(e.getLocalizedMessage());
		}
	}

	@Override
	public BaseResult uploadBase64(String base64, String fileName) {
		return uploadBase64(base64, null, fileName);
	}

	@Override
	public BaseResult uploadBase64(String base64, String catalog, String fileName) {
		Base64.Decoder decoder = Base64.getDecoder();
		byte[] bytes = decoder.decode(base64);
		InputStream inputStream = new ByteArrayInputStream(bytes);
		return BaseResult.ok(upload(inputStream, catalog, fileName));
	}

	@Override
	public BaseResult removeFile(String fileName) {
		return removeFiles(Arrays.asList(fileName));
	}

	@Override
	public BaseResult removeFiles(List<String> fileNames) {
		try {
			COSClient cosClient = createCOSClient();
			DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(tencentOssConfigModel.getBuckName());
			List<DeleteObjectsRequest.KeyVersion> keyList = new ArrayList<>();
			String fileUrlPrefix = String.format("http://%s.cos.%s.myqcloud.com/%s/",
					tencentOssConfigModel.getBuckName(), tencentOssConfigModel.getOssEndPort(),
					tencentOssConfigModel.getRoot());
			for (String fileName : fileNames) {
				String fileKey = fileName.replace(fileUrlPrefix, "");
				fileKey = tencentOssConfigModel.getRoot() + "/" + fileKey;
				keyList.add(new DeleteObjectsRequest.KeyVersion(fileKey));
			}
			deleteObjectsRequest.setKeys(keyList);
			DeleteObjectsResult deleteObjectsResult = cosClient.deleteObjects(deleteObjectsRequest);
			deleteObjectsResult.getDeletedObjects();
			LOGGER.info("文件删除成功");
			return BaseResult.ok();
		}
		catch (MultiObjectDeleteException mde) {
			List<MultiObjectDeleteException.DeleteError> deleteErrors = mde.getErrors();
			System.err.println(JSON.toJSONString(deleteErrors));
			LOGGER.error(S1, JSON.toJSONString(deleteErrors));
			return BaseResult.error("部分删除成功部分失败");
		}
		catch (CosServiceException e) {
			LOGGER.error(S1, e.getErrorMessage());
			return BaseResult.error(e.getMessage());
		}
		catch (CosClientException e) {
			LOGGER.error("发生异常", e);
			return BaseResult.error(e.getMessage());
		}
	}

	/**
	 * 创建 COSClient 实例，这个实例用来后续调用请求
	 * @return 结果
	 */
	COSClient createCOSClient() {
		COSCredentials cred = new BasicCOSCredentials(tencentOssConfigModel.getAppKey(),
				tencentOssConfigModel.getAppSecret());
		Region region = new Region(this.tencentOssConfigModel.getOssEndPort());
		ClientConfig clientConfig = new ClientConfig(region);
		clientConfig.setHttpProtocol(HttpProtocol.https);
		COSClient cosClient = new COSClient(cred, clientConfig);
		return cosClient;
	}

	TransferManager createTransferManager() {
		COSClient cosClient = createCOSClient();
		ExecutorService threadPool = new ThreadPoolExecutor(2, 5, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3),
				Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());
		TransferManager transferManager = new TransferManager(cosClient, threadPool);
		TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
		transferManagerConfiguration.setMultipartUploadThreshold(5 * 1024 * 1024);
		transferManagerConfiguration.setMinimumUploadPartSize(1 * 1024 * 1024);
		transferManager.setConfiguration(transferManagerConfiguration);

		return transferManager;
	}

	void shutdownTransferManager(TransferManager transferManager) {
		transferManager.shutdownNow(true);
	}

}
